/*
 * adc.c
 *
 * Created: 6/18/2013 6:53:08 PM
 *  Author: Tim
 */ 


#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>

#include "adc.h"
#include "../allocations.h"
#include "../../board/board.h"
#include "../../packets/structs/adcPacketStructs.h"
#include "../tmr/tmr.h"



void adcSetup()
{
	//enable the peripheral clock for the adc module(s)
	prEnable(ADC_PR);
	
	ADCA.CALL = ReadCalibrationByte( offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0) );
	ADCA.CALH = ReadCalibrationByte( offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1) );
	//delay of analog startup
	MCU.ANAINIT = MCU_STARTUPDLYA0_bm | MCU_STARTUPDLYA1_bm;
	
	//select the 2.5V external reference
	ADCA.REFCTRL	=	ADC_REFSEL_AREFB_gc		;
	
	//ADC prescaler selection
	ADCA.PRESCALER = ADC_PRESCALER_DIV32_gc;
	
	//setup the ADCA configuration
	ADCA.CTRLB		=
	ADC_CURRLIMIT_NO_gc		|	//eliminate the current limit in order to max out the ADC speed
	ADC_CONMODE_bm			|	//sets up the conversions for signed mode
	ADC_FREERUN_bm			|	//sets up the adc to collect data autonomously
	ADC_RESOLUTION_12BIT_gc	;	//sets adc to have 12 bit resolution with right adjustment (LSB)
	
	//tell the ADC to speep all channels 0-3 automatically
	ADCA.EVCTRL = ADC_SWEEP_0123_gc;
	
	/**************Channel setups***************/
	
	
	//preliminary gain and input mode selection
	
	ADCA.CH0.CTRL	 = ADC_CH_GAIN_2X_gc | ADC_CH_INPUTMODE_DIFFWGAIN_gc;

	ADCA.CH1.CTRL	 = ADC_CH_GAIN_2X_gc | ADC_CH_INPUTMODE_DIFFWGAIN_gc;

	ADCA.CH2.CTRL	 = ADC_CH_GAIN_2X_gc | ADC_CH_INPUTMODE_DIFFWGAIN_gc;

	ADCA.CH3.CTRL	 = ADC_CH_GAIN_2X_gc | ADC_CH_INPUTMODE_DIFFWGAIN_gc;

	// mux positions
	
	ADCA.CH2.MUXCTRL =	ADC_CH_MUXPOS_PIN3_gc	|
	ADC_CH_MUXNEG_PIN7_gc	;
	
	ADCA.CH3.MUXCTRL =	ADC_CH_MUXPOS_PIN0_gc	|
	ADC_CH_MUXNEG_PIN4_gc	;
	
	ADCA.CH1.MUXCTRL =	ADC_CH_MUXPOS_PIN2_gc	|
	ADC_CH_MUXNEG_PIN6_gc	;
	
	ADCA.CH0.MUXCTRL =	ADC_CH_MUXPOS_PIN1_gc	|
	ADC_CH_MUXNEG_PIN5_gc	;
	
	
#ifndef ADC_DMA_MODE
	
	ADCA.CTRLA |= ADC_ENABLE_bm;
	
	//set the appropriate interrupt levels at the global scope
	PMIC.CTRL |= PMIC_LOLVLEN_bm | PMIC_RREN_bm;
#else
//DMA method
	prEnable(DMA_PR);
	
	DMA.CTRL = DMA_ENABLE_bm | DMA_DBUFMODE_DISABLED_gc | DMA_PRIMODE_RR0123_gc; 
	
	DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_ADCA_CH4_gc;	//trigger on ch 0,1,2,3
	
	DMA.CH0.ADDRCTRL =	DMA_CH_SRCRELOAD_BURST_gc			|	//reload adc source address after reading all channels per the burst
						DMA_CH_SRCDIR_INC_gc				|	//increment source address'
						DMA_CH_DESTRELOAD_BLOCK_gc			|	//reload destination address after transferring all samples
						DMA_CH_DESTDIR_INC_gc;					//increment destination address'
						
	
	DMA.CH0.TRFCNT = 128;	//eight bytes for 4 adc channels
	DMA.CH0.REPCNT = 0;	// 16 samples deep
		
	DMA.CH0.SRCADDR0 = (uint16_t)(&ADCA_CH0RES);
	DMA.CH0.SRCADDR1 = ((uint16_t)(&ADCA_CH0RES))>>8;
	DMA.CH0.SRCADDR2 = 0;
	
	DMA.CH0.DESTADDR0 = (uint16_t)(&ADC_DMA_data[0][0]);
	DMA.CH0.DESTADDR1 = ((uint16_t)(&ADC_DMA_data[0][0]))>>8;
	DMA.CH0.DESTADDR2 = 0;
	
	DMA.CH0.CTRLA = DMA_CH_REPEAT_bm |
					DMA_CH_ENABLE_bm |
	//				DMA_CH_SINGLE_bm |
					DMA_CH_BURSTLEN_8BYTE_gc;
		
	ADCA.CTRLA = ADC_DMASEL_CH01_gc | ADC_ENABLE_bm;
//	DMA.CH0.CTRLB = DMA_CH_TRNINTLVL_LO_gc;
#endif
	
	ADCA.CH0.CTRL |= ADC_CH_START_bm;
	ADCA.CH1.CTRL |= ADC_CH_START_bm;
	ADCA.CH2.CTRL |= ADC_CH_START_bm;
	ADCA.CH3.CTRL |= ADC_CH_START_bm;
	
}

ISR(DMA_CH0_vect)
{
	DMA.CH0.TRFCNT = 8;	//eight bytes for 4 adc channels
	DMA.CH0.REPCNT = 16; // 16 samples deep
	
	DMA.CH0.CTRLA = DMA_CH_REPEAT_bm |
					DMA_CH_ENABLE_bm ;
}

int16_t filterData(int16_t *data)
{
	int32_t sum = 0;
	uint8_t inc;	//may want to make this a 16-bit number soon

for(inc=0;inc<64;inc+=4)
	{
		sum+=data[inc];
	}

	if (sum < 0)
	{
		sum = sum>>2;
		sum |= 0xC0000000;
	}
	else
	{
		sum = sum>>2;
	}	
	return (int16_t) sum;
}

void adcSetGain(ACHM_t achm, uint8_t gain)
{
	
	if(achm & ADC_CH0_bp)
	{
		ADCA.CH0.CTRL &= ~(ADC_CH_GAIN_gm);
		ADCA.CH0.CTRL |= (gain<<ADC_CH_GAIN_gp);
		adcSetChGainOffset(0,gain);
	}
	if(achm & ADC_CH1_bp)
	{
		ADCA.CH1.CTRL &= ~(ADC_CH_GAIN_gm);
		ADCA.CH1.CTRL |= (gain<<ADC_CH_GAIN_gp);
		adcSetChGainOffset(1,gain);
	}
	if(achm & ADC_CH2_bp)
	{
		ADCA.CH2.CTRL &= ~(ADC_CH_GAIN_gm);
		ADCA.CH2.CTRL |= (gain<<ADC_CH_GAIN_gp);
		adcSetChGainOffset(2,gain);
	}
	if(achm & ADC_CH3_bp)
	{
		ADCA.CH3.CTRL &= ~(ADC_CH_GAIN_gm);
		ADCA.CH3.CTRL |= (gain<<ADC_CH_GAIN_gp);
		adcSetChGainOffset(3,gain);
	}
}

void adcSetChGainOffset(uint8_t channel,uint8_t gain)
{
	switch(gain)
	{
		case (ADC_CH_GAIN_2X_gc>>ADC_CH_GAIN_gp):
		daqCal.ADC_CH_OFFSET[channel] = daqCal.ADC_OFFSET_CAL_2X[channel];
		break;
		case (ADC_CH_GAIN_4X_gc>>ADC_CH_GAIN_gp):
		daqCal.ADC_CH_OFFSET[channel] = daqCal.ADC_OFFSET_CAL_4X[channel];
		break;
		case (ADC_CH_GAIN_8X_gc>>ADC_CH_GAIN_gp):
		daqCal.ADC_CH_OFFSET[channel] = daqCal.ADC_OFFSET_CAL_8X[channel];
		break;
		case (ADC_CH_GAIN_16X_gc>>ADC_CH_GAIN_gp):
		daqCal.ADC_CH_OFFSET[channel] = daqCal.ADC_OFFSET_CAL_16X[channel];
		break;
		case (ADC_CH_GAIN_32X_gc>>ADC_CH_GAIN_gp):
		daqCal.ADC_CH_OFFSET[channel] = daqCal.ADC_OFFSET_CAL_32X[channel];
		break;
		case (ADC_CH_GAIN_64X_gc>>ADC_CH_GAIN_gp):
		daqCal.ADC_CH_OFFSET[channel] = daqCal.ADC_OFFSET_CAL_64X[channel];
		break;
	}
}
